package com.keray.common.service.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.keray.common.Wrappers;
import com.keray.common.cache.CacheConstants;
import com.keray.common.entity.BSService;
import com.keray.common.entity.IBSEntity;
import com.keray.common.service.TreeProcess;
import com.keray.common.service.model.base.BaseTreeModel;
import org.springframework.cache.annotation.Cacheable;

import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author by keray
 * date:2019/8/16 16:17
 * 树形结构实体的操作服务
 */
public interface ITreeModelService<T extends BaseTreeModel<T, ID>, ID extends Serializable> extends BSService<T, ID> {

    /**
     * <p>
     * <h3>>作者 keray</h3>
     * <h3>>时间： 2019/8/16 16:08</h3>
     * 具有上下级的实体 设置树结构
     * 仅当n=-1时缓存
     * </p>
     *
     * @param parent
     * @param n      获取多少级 -1 不限
     * @return <p>  </p>
     */
    @Cacheable(value = CacheConstants.BIG_UP_UP, key = "'tree:' + #parent.id + #n", unless = "#result == null")
    default T setChildren(T parent, int n) {
        try {
            return setChildren(parent, n, Wrappers.query());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    default T setChildrenNoCache(T parent, int n) {
        try {
            return setChildren(parent, n, Wrappers.query());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * <p>
     * <h3>>作者 keray</h3>
     * <h3>>时间： 2019/8/16 16:08</h3>
     * 具有上下级的实体 设置树结构
     * </p>
     *
     * @param parent
     * @param n      获取多少级 -1 不限
     * @return <p>  </p>
     */
    default T setChildren(T parent, int n, QueryWrapper<T> inputQueryWrapper) {
        if (n == 0) {
            return parent;
        }
        List<T> children;
        try {
            QueryWrapper<T> queryWrapper = inputQueryWrapper == null ? Wrappers.query() : inputQueryWrapper.clone();
            children = getMapper().selectList(queryWrapper
                    .select("id")
                    .eq("parent_id", parent.getId()));
            if (CollUtil.isEmpty(children)) {
                return parent;
            }
            children = children.stream()
                    .parallel()
                    .map(c -> this.modelDetail(c.getId()))
                    .collect(Collectors.toList());
            parent.setChildren(children);
            for (T c : children) {
                // 深copy父节点 避免循环依赖
                T parentCopy = (T) parent.getClass().getDeclaredConstructor().newInstance();
                BeanUtil.copyProperties(parent, parentCopy, "parent", "children");
                c.setParent(parentCopy);
                setChildren(c, n - 1, inputQueryWrapper);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return parent;
    }

    default List<ID> allChildrenIds(ID parentId, int n, QueryWrapper<T> inputQueryWrapper, List<ID> result) {
        if (n == 0) {
            return result;
        }
        QueryWrapper<T> queryWrapper = inputQueryWrapper == null ? Wrappers.query() : inputQueryWrapper.clone();
        List<ID> childrenIds = getMapper().selectList(queryWrapper
                .select("id")
                .eq("parent_id", parentId)).stream().map(IBSEntity::getId).collect(Collectors.toList());
        if (CollUtil.isEmpty(childrenIds)) {
            return result;
        }
        result.addAll(childrenIds);
        for (ID id : childrenIds) {
            allChildrenIds(id, n - 1, inputQueryWrapper, result);
        }
        return result;
    }

    default List<ID> allChildrenIds(ID parentId) {
        return allChildrenIds(parentId, -1, null, new LinkedList<>());
    }

    /**
     * <p>
     * <h3>作者 keray</h3>
     * <h3>时间： 2019/10/25 17:24</h3>
     * 属性结构下一级节点
     * </p>
     *
     * @param parent
     * @return <p> {@link List<String>} </p>
     * @throws
     */
    default List<ID> selectNextChildrenIds(T parent) {
        return getMapper()
                .selectList(
                        Wrappers.<T>query()
                                .select("id")
                                .eq("parent_id", parent.getId()))
                .stream()
                .map(T::getId)
                .collect(Collectors.toList());
    }


    /**
     * <p>
     * <h3>作者 keray</h3>
     * <h3>时间： 2019/10/25 17:24</h3>
     * 属性结构下一级节点的id
     * </p>
     *
     * @param parent
     * @return <p> {@link List<T>} </p>
     * @throws
     */
    default List<T> selectNextChildren(T parent) {
        return getMapper().selectList(Wrappers.<T>query()
                .in("parent_id", parent.getId()));
    }


    default T allParent(T owner) {
        return allParent(owner, (Wrapper<T>) null);
    }

    default T allParent(T owner, Wrapper<T> wrapper) {
        if (ObjectUtil.isEmpty(owner.getParentId())) {
            return owner;
        }
        T parent = oneParent(owner.getParentId(), wrapper);
        owner.setParent(parent);
        allParent(parent);
        return owner;
    }

    default List<T> allParent(T owner, List<T> result) {
        return allParent(owner, result, null);
    }

    default List<T> allParent(T owner, List<T> result, Wrapper<T> wrapper) {
        if (result == null) {
            result = new LinkedList<>();
        }
        if (ObjectUtil.isEmpty(owner.getParentId())) {
            return result;
        }
        T parent = oneParent(owner.getParentId(), wrapper);
        result.add(parent);
        return allParent(parent, result, wrapper);
    }

    default List<ID> allParentIds(ID parentId, List<ID> result) {
        return allParentIds(parentId, result, null);
    }

    default List<ID> allParentIds(ID parentId, List<ID> result, Wrapper<T> wrapper) {
        if (result == null) {
            result = new LinkedList<>();
        }
        if (ObjectUtil.isEmpty(parentId)) {
            return result;
        }
        if (wrapper == null) {
            wrapper = Wrappers.lambdaQuery();
        }
        if (wrapper instanceof LambdaQueryWrapper) {
            ((LambdaQueryWrapper<T>) wrapper).select(T::getId).eq(T::getId, parentId);
        } else {

            ((QueryWrapper<T>) wrapper).select("id").eq("id", parentId);
        }
        T parent = selectOne(wrapper);
        result.add(parent.getId());
        return allParentIds(parent.getParentId(), result, wrapper);
    }

    default T oneParent(ID parentId) {
        return oneParent(parentId, null);
    }

    default T oneParent(ID parentId, Wrapper<T> wrapper) {
        if (wrapper == null) {
            wrapper = Wrappers.lambdaQuery();
        }
        if (wrapper instanceof LambdaQueryWrapper) {
            return selectOne(((LambdaQueryWrapper<T>) wrapper).eq(T::getId, parentId));
        }
        return selectOne(((QueryWrapper<T>) wrapper).eq("id", parentId));
    }


    /**
     * <p>
     * <h3>作者 keray</h3>
     * <h3>时间： 2019/10/25 17:24</h3>
     * 节点详情获取
     * </p>
     *
     * @return <p> {@link T} </p>
     * @throws
     */
    default T modelDetail(ID id) {
        return getById(id);
    }

    default void treeProcess(T parent, TreeProcess<T> process) {
        List<T> children = parent.getChildren();
        if (CollUtil.isNotEmpty(children)) {
            for (T child: children) {
                process.apply(child, parent);
                treeProcess(child, process);
            }
        }
    }


}
